Skip to content

[4주차] 강지훈/[feat] 액션 중심 API 구현#152

Merged
theSnackOverflow merged 3 commits intoLeets-Official:강지훈/mainfrom
theSnackOverflow:강지훈/4주차
May 3, 2026

Hidden character warning

The head ref may contain hidden characters: "\uac15\uc9c0\ud6c8/4\uc8fc\ucc28"
Merged

[4주차] 강지훈/[feat] 액션 중심 API 구현#152
theSnackOverflow merged 3 commits intoLeets-Official:강지훈/mainfrom
theSnackOverflow:강지훈/4주차

Conversation

@theSnackOverflow
Copy link
Copy Markdown
Member

@theSnackOverflow theSnackOverflow commented Apr 28, 2026

1. 과제 요구사항 중 구현한 내용

4주차 학습 주제: REST/RESTful API와 좋은 API 설계 — 단순 자원 CRUD를 넘어 도메인의 비즈니스 행위를 액션 중심 엔드포인트(POST /자원/{id}/액션)로 표현

스스로 정의한 요구사항

  • 게시물 신고: 동일 사용자가 같은 게시물을 중복 신고하거나, 자신의 게시물을 신고할 수 없다
  • 댓글 신고: 동일 사용자가 같은 댓글을 중복 신고하거나, 자신의 댓글을 신고할 수 없다
  • 신고 처리: 신고를 처리 완료로 전환하며, 이미 처리된 신고는 재처리 불가
  • 게시물 숨김: 작성자만 자신의 게시물을 숨길 수 있으며, 이미 숨겨진 게시물은 중복 숨김 불가
  • 댓글 채택: 게시물 작성자만 채택 가능, 게시물당 채택은 1개이며 기존 채택 댓글은 자동 해제

구현한 액션 API

  • POST /api/v1/posts/{postId}/reports — 게시물 신고 (PENDING 생성, 중복·자기 신고 차단)
  • POST /api/v1/comments/{commentId}/reports — 댓글 신고 (PENDING 생성, 중복·자기 신고 차단)
  • POST /api/v1/reports/{reportId}/resolve — 신고 처리 완료 (PENDING → RESOLVED)
  • POST /api/v1/posts/{postId}/hide — 게시물 숨김 (PUBLISHED → HIDDEN, 작성자 전용)
  • POST /api/v1/posts/{postId}/comments/{commentId}/accept — 댓글 채택 (1게시물 1채택, 기존 채택 자동 해제)

2. 핵심 변경 사항

도메인 상태 타입 도입

  • ReportStatus enum 추가 (PENDING / RESOLVED)
  • PostStatus enum 추가 (PUBLISHED / HIDDEN)
  • Post.status 필드: StringPostStatus enum으로 교체
  • Report 엔티티에 status, resolvedBy, resolvedAt 필드 및 resolve(User) 도메인 메서드 추가
  • Comment 엔티티에 accepted 필드 및 accept() / unaccept() 도메인 메서드 추가

중복 방지 로직

  • Report 테이블에 DB 유니크 제약 추가: (reporter_id, post_id), (reporter_id, comment_id)
  • ReportRepositoryexistsByReporter_IdAndPost_Id, existsByReporter_IdAndComment_Id 추가
  • CommentRepositoryfindByPost_IdAndAcceptedTrue 추가

신규 ErrorCode

코드 HTTP 메시지
R002 409 이미 신고한 대상입니다.
R003 400 자신의 게시물/댓글은 신고할 수 없습니다.
R004 409 이미 처리된 신고입니다.
P002 409 이미 숨김 처리된 게시물입니다.
C002 400 유효하지 않은 부모 댓글입니다.

기존 코드 개선

  • 신고 API 재구성: 기존 POST /api/v1/reports (targetType 분기) → 자원별 하위 경로로 분리
  • 부모 댓글 검증 오류 코드 INVALID_REPORT_TARGETINVALID_PARENT_COMMENT 교체

3. 실행 및 검증 결과

실행: ./gradlew bootRun 후 정상 기동 확인 (port 8080)

게시물 신고 POST /api/v1/posts/1/reports

// 응답 (201)
{
  "status": "success",
  "data": {
    "report_id": 1,
    "target_type": "POST",
    "reason": "스팸성 게시물입니다",
    "status": "PENDING",
    "resolved_at": null
  }
}

중복 신고 차단

// 응답 (409)
{ "status": "error", "message": "이미 신고한 대상입니다.", "data": null }

신고 처리 완료 POST /api/v1/reports/1/resolve

// 응답 (200)
{
  "status": "success",
  "data": {
    "report_id": 1,
    "status": "RESOLVED",
    "resolved_at": "2026-04-28T21:13:11.958942"
  }
}

이미 처리된 신고 재처리 차단

// 응답 (409)
{ "status": "error", "message": "이미 처리된 신고입니다.", "data": null }

게시물 숨김 POST /api/v1/posts/1/hide

// 응답 (200)
{
  "status": "success",
  "data": { "post_id": 1, "status": "HIDDEN" }
}

댓글 채택 POST /api/v1/posts/1/comments/1/accept

// 응답 (200)
{
  "status": "success",
  "data": { "comment_id": 1, "accepted": true }
}

4. 완료 사항

  1. 도메인 상태 변화 액션 API 5개 구현 (게시물 신고, 댓글 신고, 신고 처리, 게시물 숨김, 댓글 채택)
  2. 중복 처리 방지: DB 유니크 제약 + 애플리케이션 레벨 이중 검증
  3. 자기 자신 신고 차단, 권한 외 접근 시 403 처리
  4. 도메인 메서드(resolve, hide, accept, unaccept)로 상태 변환 캡슐화
  5. ./gradlew build 성공 및 모든 시나리오 수동 검증 완료

5. 추가 사항

  • Swagger UI: http://localhost:8080/swagger-ui.html
  • H2 콘솔: http://localhost:8080/h2-console (JDBC URL: jdbc:h2:mem:blog)

제출 체크리스트

  • PR 제목이 규칙에 맞다
  • base가 강지훈/main 브랜치다
  • compare가 강지훈/4주차 브랜치다
  • 프로젝트가 정상 실행된다
  • 본인을 Assignee로 지정했다
  • 파트 담당 Reviewer를 지정했다
  • 리뷰 피드백을 반영한 뒤 머지/PR close를 진행한다

Reviewer 참고

- ReportStatus enum 추가 (PENDING/RESOLVED)
- Report 엔티티에 status, resolvedBy, resolvedAt 필드 및 resolve() 도메인 메서드 추가
- DB 유니크 제약으로 중복 신고 방지 (reporter+post, reporter+comment)
- 자기 자신 신고 차단 로직 추가
- POST /api/v1/posts/{postId}/reports — 게시물 신고
- POST /api/v1/comments/{commentId}/reports — 댓글 신고
- POST /api/v1/reports/{reportId}/resolve — 신고 처리 완료 (PENDING→RESOLVED)
- 기존 targetType 기반 단일 엔드포인트 제거
- PostStatus enum 추가 (PUBLISHED/HIDDEN)
- Post 엔티티의 status 필드를 문자열에서 PostStatus enum으로 교체
- hide() 도메인 메서드 추가 (이미 HIDDEN이면 409 반환)
- POST /api/v1/posts/{postId}/hide — 게시물 숨김 (작성자 전용)
- 게시물 신고 엔드포인트 PostController로 통합
- Comment 엔티티에 accepted 필드 및 accept()/unaccept() 도메인 메서드 추가
- POST /api/v1/posts/{postId}/comments/{commentId}/accept — 댓글 채택
  - 게시물 작성자만 채택 가능
  - 게시물당 채택은 1개 (기존 채택 댓글은 자동 해제 후 신규 채택)
  - 이미 채택된 동일 댓글 재요청 시 멱등 200 반환
- 댓글 신고 엔드포인트 CommentController로 통합
- 부모 댓글 검증 오류 코드 INVALID_REPORT_TARGET → INVALID_PARENT_COMMENT 교체
@theSnackOverflow theSnackOverflow requested a review from a team April 28, 2026 12:18
@theSnackOverflow theSnackOverflow self-assigned this Apr 28, 2026
@daekyochung
Copy link
Copy Markdown

도메인 상태 변화(PENDING/RESOLVED, PUBLISHED/HIDDEN)를 명확하게 구분하고, PATCH /api/reports/{reportId}/resolve로 신고를 처리하는 로직도 체계적인것 같습니다! HTTP 상태코드(201, 400, 409 등)를 적절하게 사용하고, 다양한 예외 상황(중복 신고, 잘못된 요청)을 처리한 것이 인상적입니다. 배워갑니다

@sky-0131
Copy link
Copy Markdown

기능에 맞춰 에러 코드와 도메인 상태를 체계적으로 도식화하셔서, 신규 기능을 이해하는 데 필요한 시간이 적었습니다. 신고 기능에서 reporter_id를 도입하여 직관성을 높이고, 읽는 사람이 무엇을 신고하는지 확인하는 로직임을 좀 더 알기 쉬워 보입니다. 또 신고자와 피신고자를 구분해야 하는 상황에서 이런 명명 방식이 더 효과적일 것 같습니다. 오늘도 많이 배워갑니다.

@yukyoungs
Copy link
Copy Markdown

코드 잘 봤습니다! 이번 주차 핵심인 액션 중심 엔드포인트 설계를 정말 깔끔하게 잘해주셨네용
특히 엔티티 내부에서 resolve(), accept() 등의 도메인 메서드를 사용하여 비즈니스 로직을 캡슐화한 부분이 객체지향적인 설계라고 생각합니다..

@theSnackOverflow theSnackOverflow merged commit b743c1c into Leets-Official:강지훈/main May 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants